package com.atguigu.gmall.order.controller;

import com.atguigu.gmall.cart.client.CartFeignClient;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.util.AuthContextHolder;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;


import com.atguigu.gmall.order.service.OrderService;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * ClassName: OrderApiController
 * Package: com.atguigu.gmall.order.controller
 * Description:
 *
 * @Author 刘轩
 * @Create 2023-09-02 9:39
 * @Version 1.0
 */

@RestController
@RequestMapping("/api/order")
public class OrderApiController {

    @Autowired
    private CartFeignClient cartFeignClient;
    @Autowired
    private OrderService orderService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;


    //  这个控制器是给web-all 使用，需要让web-all 展示数据。
    @GetMapping("/auth/trade")
    public Result authTrade(HttpServletRequest request) {
        //  获取用户Id
        String userId = AuthContextHolder.getUserId(request);
        //  封装数据到map中.
        HashMap<String, Object> map = new HashMap<>();
        //  订单明细来自于购物车选中的商品列表
        List<CartInfo> cartCheckedList = cartFeignClient.getCartCheckedList(Long.parseLong(userId));
        //  表示具有原子性 底层源码使用的是啥 CAS
        AtomicInteger totalNum = new AtomicInteger();
        //  需要将cart--->orderDetail;
        List<OrderDetail> orderDetailList = cartCheckedList.stream().map(cartInfo -> {
            //  赋值
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderPrice(cartInfo.getSkuPrice());
            orderDetail.setSkuNum(cartInfo.getSkuNum());
            orderDetail.setSkuName(cartInfo.getSkuName());
            orderDetail.setImgUrl(cartInfo.getImgUrl());
            //  细节是需要给skuId
            orderDetail.setSkuId(cartInfo.getSkuId());
            totalNum.addAndGet(cartInfo.getSkuNum());
            return orderDetail;
        }).collect(Collectors.toList());

        //  orderInfo 中有计算总金额。
        OrderInfo orderInfo = new OrderInfo();
        //  赋值订单明细.
        orderInfo.setOrderDetailList(orderDetailList);
        orderInfo.sumTotalAmount();
        //  存储数据
        map.put("detailArrayList",orderDetailList);
        map.put("totalNum",totalNum);
        map.put("totalAmount",orderInfo.getTotalAmount());
        //  存储流水号
        map.put("tradeNo",orderService.getTradeNo(userId));
        //  返回map
        return Result.ok(map);
    }


    /**
     * 提交订单
     *
     * @param orderInfo
     * @param request
     * @return
     */
    @PostMapping("auth/submitOrder")
    public Result submitOrder(@RequestBody OrderInfo orderInfo, HttpServletRequest request) {
        //  获取到用户Id  校验库存，校验价格，防止重复提交订单，优惠券{ 1.哪些商品参与了满减活动 2. 计算哪个活动更适合你} 满减 {1000 200 2000 500}
        String userId = AuthContextHolder.getUserId(request);
        orderInfo.setUserId(Long.parseLong(userId));
        //  下单之前防止重复提交！ tradeNo 页面存储一个流水号！
        //  先获取页面传递的流水号
        String tradeNo = request.getParameter("tradeNo");
        //  使用页面的流水号与缓存的流水号进行比较！
        //  Boolean result = this.orderService.checkTradeNo(tradeNo, userId);
        //  if (!result){
        //      return Result.fail().message("用不能回退无刷新重复提交");
        //  }
        //  //  比较完成之后，删除
        //  this.orderService.delTradeNo(userId);

        //  lua 脚本
        String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";

        String tradeNoKey = "tradeNo:"+userId;
        //  执行lua 脚本：
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(scriptText);
        redisScript.setResultType(Long.class);
        //  第一个参数 redisScript 对象，第二个参数锁的key，第三个参数是key 对应的value
        Long result = (Long) this.redisTemplate.execute(redisScript, Arrays.asList(tradeNoKey), tradeNo);
        //  判断
        if (result.intValue() == 0){
            return Result.fail().message("用不能回退无刷新重复提交");
        }

        //  校验商品
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        //  多线程优化下单：
        List<CompletableFuture> completableFutureList = new ArrayList<>();
        //  声明一个错误信息集合
        List<String> errorList = new ArrayList<>();

        //  循环遍历订单明细
        if (!CollectionUtils.isEmpty(orderDetailList)){
            for (OrderDetail orderDetail : orderDetailList) {
                //  调用服务层方法
                CompletableFuture<Void> stockCompletableFuture = CompletableFuture.runAsync(() -> {
                    Boolean isExist = this.orderService.checkStock(orderDetail.getSkuId(), orderDetail.getSkuNum());
                    if (!isExist) {
                        errorList.add(orderDetail.getSkuId() + "库存不足，请重新下单");
                    }
                },threadPoolExecutor);
                //  将这个线程添加到集合中
                completableFutureList.add(stockCompletableFuture);
                //  遍历
                CompletableFuture<Void> priceCompletableFuture = CompletableFuture.runAsync(() -> {
                    //  订单价格与实时价格做比较
                    BigDecimal orderPrice = orderDetail.getOrderPrice(); // cartInfo.skuPrice();
                    BigDecimal skuPrice = this.productFeignClient.getSkuPrice(orderDetail.getSkuId());
                    //  有没有变动

                },threadPoolExecutor);
                //  将这个线程添加到集合中
                completableFutureList.add(priceCompletableFuture);
            }
        }

        //  多任务组合 集合变数组;
        CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[completableFutureList.size()])).join();
        //  判断是否有错误信息
        if (errorList.size()>0){
            //  "",""
            return Result.fail().message(StringUtils.join(errorList,","));
        }
        //  调用服务层方法.
        Long orderId = this.orderService.saveOrderInfo(orderInfo);
        //  将订单Id 返回
        return Result.ok(orderId);
    }

    //  /auth/${page}/${limit}?orderStatus=UNPAID
    //  查看我的订单

    /**
     * h5 端
     * @param page
     * @param limit
     * @param request
     * @return
     */
    @GetMapping("/auth/{page}/{limit}")
    public Result getOrderPageList(@PathVariable Long page,
                                   @PathVariable Long limit,
                                   HttpServletRequest request){
        //  获取用户Id
        String userId = AuthContextHolder.getUserId(request);
        //  直接获取到订单状态 网页版本的时候，没有传递这个参数。所以你在获取的时候是null.
        String orderStatus = request.getParameter("orderStatus");

        //  分页对象
        Page<OrderInfo> orderInfoPage = new Page<>(page,limit);
        //  调用服务层方法.
        IPage<OrderInfo> iPage = this.orderService.getOrderPageList(orderInfoPage,userId,orderStatus);
        return Result.ok(iPage);
    }
/**
 * 内部调用获取订单      根据订单id 获取数据
 * @param orderId
 * @return
 */
@GetMapping("/inner/getOrderInfo/{orderId}")
    public OrderInfo getOrderInfo(@PathVariable Long orderId){
    return orderService.getOrderInfo(orderId);
}

// 拆分业务
    //  http://localhost:8204/api/order/orderSplit?orderId=xxx&wareSkuMap=xxx
    //  由于返回的数据只需要9个字段，orderInfo 不只有九个字段。所以，将orderInfo 转换Map
    @PostMapping("/orderSplit")
    public List<Map> orderSpilt(HttpServletRequest request) {
        //  获取请求参数
        String orderId = request.getParameter("orderId");
        String wareSkuMap = request.getParameter("wareSkuMap");
     // 获取到订单集合，然后将其转换为list<Map>
    List<OrderInfo>  orderInfoList =  orderService.orderSpilt(orderId,wareSkuMap);

    // 进行转换
        if(!CollectionUtils.isEmpty(orderInfoList)){
            List<Map> mapList = orderInfoList.stream().map(orderInfo -> {
                Map map = orderService.initWare(orderInfo);
                return map;
            }).collect(Collectors.toList());
            return mapList;
        }
        return new ArrayList<>();
    }
}
